SciChart.js JavaScript 2D Charts API > 2D Chart Types > Series PaletteProvider API > Per-Point Colouring of Candlestick / OHLC Charts
Per-Point Colouring of Candlestick / OHLC Charts

Candlestick series can be colored per-point or per line-segment using the PaletteProvider API. To use this, we must create a class (typescript) or object (javascript) which implements or confirms to the IStrokePaletteProvider and IFillPaletteProvider interfaces. Then, apply this to the FastCandlestickRenderableSeries.paletteProvider property. This allows you to colour data-points based on values, or custom rules with infinite extensiblity.

First let's create the PaletteProvider class.

const { DefaultPaletteProvider, parseColorToUIntArgb } = SciChart;

// Create a class which inherits DefaultPaletteProvider
class CandlePaletteProvider extends DefaultPaletteProvider {

  constructor() {
    super();
    this.highlightColor = parseColorToUIntArgb("#FEFEFE");
  }

  // Override onAttached to get the parent FastCandlestickRenderableSeries
  onAttached(parentSeries) {
    this.parentSeries = parentSeries;
  }

  // This function is called for every data-point
  // Return undefined to use the default color for the fill, else, return
  // a custom color as ARGB color code e.g. 0xFFFF0000 is red
  overrideFillArgb(xValue, yValue, index, opacity, metadata) {
    return this.overrideColorAt(index, this.parentSeries.dataSeries);
  }
  overrideStrokeArgb(xValue, yValue, index, opacity, metadata) {
    return this.overrideColorAt(index, this.parentSeries.dataSeries);
  }

  overrideColorAt(index, ohlcDataSeries) {
    // Get the open, close values
    const close = ohlcDataSeries.getNativeCloseValues().get(index);
    const open = ohlcDataSeries.getNativeOpenValues().get(index);
    // If more than 1% change, return 'highlightColor' otherwise return undefined for default color
    if (Math.abs(1 - (open / close)) > 0.01) {
      return this.highlightColor;
    }
    return undefined;
  }
}

Next, apply the PaletteProvider to the Candlestick series. 

const candlestickSeries = new FastCandlestickRenderableSeries(wasmContext, {
  dataSeries: new OhlcDataSeries(wasmContext, {
    xValues: dateValues,
    openValues,
    highValues,
    lowValues,
    closeValues,
  }),
  strokeThickness: 1,
  dataPointWidth: 0.7,
  brushUp: "#33ff3377",
  brushDown: "#ff333377",
  strokeUp: "#77ff77",
  strokeDown: "#ff7777",
  // Attach a paletteprovider here. Candles with > 1% range will be highlighted
  paletteProvider: new CandlePaletteProvider()
});
sciChartSurface.renderableSeries.add(candlestickSeries);
// Register the custom CandlestickPaletteProvider with the chartBuilder
chartBuilder.registerType(EBaseType.PaletteProvider, "CandlePaletteProvider",
    () => new CandlePaletteProvider());

const { wasmContext, sciChartSurface } = await chartBuilder.build2DChart(divElementId, {
  surface: { theme: { type: EThemeProviderType.Dark } },
  xAxes: [{ type: EAxisType.CategoryAxis }],
  yAxes: [
    { type: EAxisType.NumericAxis, options: { labelPrefix: "$", labelPrecision: 2 } },
    { type: EAxisType.NumericAxis, options: { isVisible: false, id: "VolumeAxisId", growBy: new NumberRange(0, 4) } },
  ],
  series: [
    {
      type: ESeriesType.CandlestickSeries,
      ohlcData: {
        xValues: dateValues,
        openValues,
        highValues,
        lowValues,
        closeValues
      },
      options: {
        dataPointWidth: 0.7,
        brushUp: "#33ff3377",
        brushDown: "#ff333377",
        strokeUp: "#77ff77",
        strokeDown: "#ff7777",
        strokeThickness: 1,
        // Apply the PaletteProvider to the candlestick series
        paletteProvider: {
          type: EPaletteProviderType.Custom,
          customType: "CandlePaletteProvider",
        }
      }
    },
    {
      type: ESeriesType.ColumnSeries,
      xyData: {
        xValues: dateValues,
        yValues: volumeValues,
      },
      options: {
        yAxisId: "VolumeAxisId",
        strokeThickness: 0,
        dataPointWidth: 0.7,
        opacity: 0.47
      }
    }
  ]
});

The code above results in the following output. 

<div id="scichart-root" ></div>
  
body { margin: 0; }
#scichart-root { width: 100%; height: 100vh; }
  
const getCandles = async (
    symbol,
    interval,
    limit = 300
) => {
  let url = `https://api.binance.com/api/v3/klines?symbol=${symbol}&interval=${interval}`;
  if (limit) {
    url += `&limit=${limit}`;
  }
  try {
    console.log(`SimpleBinanceClient: Fetching ${limit} candles of ${symbol} ${interval}`);
    const response = await fetch(url);
    // Returned data format is [ { date, open, high, low, close, volume }, ... ]
    const data = await response.json();
    // Map to { dateValues[], openValues[], highValues[], lowValues[], closeValues[] } expected by scichart.js
    const dateValues = [];
    const openValues = [];
    const highValues = [];
    const lowValues = [];
    const closeValues = [];
    const volumeValues = [];
    data.forEach(candle => {
      const [timestamp, open, high, low, close, volume] = candle;
      dateValues.push(timestamp / 1000); // SciChart expects Unix Timestamp / 1000
      openValues.push(parseFloat(open));
      highValues.push(parseFloat(high));
      lowValues.push(parseFloat(low));
      closeValues.push(parseFloat(close));
      volumeValues.push(parseFloat(volume));
    });
    return { dateValues, openValues, highValues, lowValues, closeValues, volumeValues };
  } catch (err) {
    console.error(err);
    return [];
  }
};


// #region ExampleA
const { DefaultPaletteProvider, parseColorToUIntArgb } = SciChart;

// Create a class which inherits DefaultPaletteProvider
class CandlePaletteProvider extends DefaultPaletteProvider {

  constructor() {
    super();
    this.highlightColor = parseColorToUIntArgb("#FEFEFE");
  }

  // Override onAttached to get the parent FastCandlestickRenderableSeries
  onAttached(parentSeries) {
    this.parentSeries = parentSeries;
  }

  // This function is called for every data-point
  // Return undefined to use the default color for the fill, else, return
  // a custom color as ARGB color code e.g. 0xFFFF0000 is red
  overrideFillArgb(xValue, yValue, index, opacity, metadata) {
    return this.overrideColorAt(index, this.parentSeries.dataSeries);
  }
  overrideStrokeArgb(xValue, yValue, index, opacity, metadata) {
    return this.overrideColorAt(index, this.parentSeries.dataSeries);
  }

  overrideColorAt(index, ohlcDataSeries) {
    // Get the open, close values
    const close = ohlcDataSeries.getNativeCloseValues().get(index);
    const open = ohlcDataSeries.getNativeOpenValues().get(index);
    // If more than 1% change, return 'highlightColor' otherwise return undefined for default color
    if (Math.abs(1 - (open / close)) > 0.01) {
      return this.highlightColor;
    }
    return undefined;
  }
}
// #endregion

async function candlestickAndVolumeChart(divElementId) {
  // Demonstrates how to create a Candlestick chart with SciChart.js
  const {
    SciChartSurface,
    CategoryAxis,
    NumericAxis,
    FastCandlestickRenderableSeries,
    FastColumnRenderableSeries,
    XyDataSeries,
    OhlcDataSeries,
    NumberRange,
    SciChartJsNavyTheme
  } = SciChart;

  // or, for npm, import { SciChartSurface, ... } from "scichart"

  const { wasmContext, sciChartSurface } = await SciChartSurface.create(divElementId, {
    theme: new SciChartJsNavyTheme()
  });
  sciChartSurface.xAxes.add(new CategoryAxis(wasmContext));
  sciChartSurface.yAxes.add(new NumericAxis(wasmContext, { labelPrefix: "$", labelPrecision: 2 }));

  // Add a secondary axis for the volume bars
  sciChartSurface.yAxes.add(new NumericAxis(wasmContext, { id: "VolumeAxisId", isVisible: false, growBy: new NumberRange(0, 4) }));

  // Fetch data. Format is { dates[], opens[], highs[], lows[], closes[], volumes[] }
  const { dateValues, openValues, highValues, lowValues, closeValues, volumeValues }
      = await getCandles("BTCUSDT", "4h", 70);

  // Add a column series to render the volume bars
  sciChartSurface.renderableSeries.add(new FastColumnRenderableSeries(wasmContext, {
    dataSeries: new XyDataSeries(wasmContext, { xValues: dateValues, yValues: volumeValues }),
    yAxisId: "VolumeAxisId",
    strokeThickness: 0,
    dataPointWidth: 0.7,
    opacity: 0.47,
  }));

  // #region ExampleB
  const candlestickSeries = new FastCandlestickRenderableSeries(wasmContext, {
    dataSeries: new OhlcDataSeries(wasmContext, {
      xValues: dateValues,
      openValues,
      highValues,
      lowValues,
      closeValues,
    }),
    strokeThickness: 1,
    dataPointWidth: 0.7,
    brushUp: "#33ff3377",
    brushDown: "#ff333377",
    strokeUp: "#77ff77",
    strokeDown: "#ff7777",
    // Attach a paletteprovider here. Candles with > 1% range will be highlighted
    paletteProvider: new CandlePaletteProvider()
  });
  sciChartSurface.renderableSeries.add(candlestickSeries);
  // #endregion

  // add interactivity for the example
  const { MouseWheelZoomModifier, ZoomPanModifier, ZoomExtentsModifier } = SciChart;
  sciChartSurface.chartModifiers.add(new MouseWheelZoomModifier({ excludedYAxisIds: ["VolumeAxisId"]}));
  sciChartSurface.chartModifiers.add(new ZoomPanModifier({ excludedYAxisIds: ["VolumeAxisId"]}));
  sciChartSurface.chartModifiers.add(new ZoomExtentsModifier());
};

candlestickAndVolumeChart("scichart-root");





async function builderExample(divElementId) {
  // Demonstrates how to create a line chart with SciChart.js using the Builder API
  const {
    chartBuilder,
    ESeriesType,
    EAxisType,
    EThemeProviderType,
    NumberRange,
    EBaseType,
    EPaletteProviderType
  } = SciChart;

  // or, for npm, import { chartBuilder, ... } from "scichart"

  // Data format is { dateValues[], openValues[], highValues[], lowValues[], closeValues[] }
  const { dateValues, openValues, highValues, lowValues, closeValues, volumeValues }
      = await getCandles("BTCUSDT", "1h", 100);

  // #region ExampleC
  // Register the custom CandlestickPaletteProvider with the chartBuilder
  chartBuilder.registerType(EBaseType.PaletteProvider, "CandlePaletteProvider",
      () => new CandlePaletteProvider());

  const { wasmContext, sciChartSurface } = await chartBuilder.build2DChart(divElementId, {
    surface: { theme: { type: EThemeProviderType.Dark } },
    xAxes: [{ type: EAxisType.CategoryAxis }],
    yAxes: [
      { type: EAxisType.NumericAxis, options: { labelPrefix: "$", labelPrecision: 2 } },
      { type: EAxisType.NumericAxis, options: { isVisible: false, id: "VolumeAxisId", growBy: new NumberRange(0, 4) } },
    ],
    series: [
      {
        type: ESeriesType.CandlestickSeries,
        ohlcData: {
          xValues: dateValues,
          openValues,
          highValues,
          lowValues,
          closeValues
        },
        options: {
          dataPointWidth: 0.7,
          brushUp: "#33ff3377",
          brushDown: "#ff333377",
          strokeUp: "#77ff77",
          strokeDown: "#ff7777",
          strokeThickness: 1,
          // Apply the PaletteProvider to the candlestick series
          paletteProvider: {
            type: EPaletteProviderType.Custom,
            customType: "CandlePaletteProvider",
          }
        }
      },
      {
        type: ESeriesType.ColumnSeries,
        xyData: {
          xValues: dateValues,
          yValues: volumeValues,
        },
        options: {
          yAxisId: "VolumeAxisId",
          strokeThickness: 0,
          dataPointWidth: 0.7,
          opacity: 0.47
        }
      }
    ]
  });
  // #endregion
};



// Uncomment this to use the builder example
  //builderExample("scichart-root");

  
When the open - close is greater than a 1% range, the candle fill is coloured white.

Using this or similar logic you can add an extra-dimension of data to JavaScript Candlestick charts.

Applying PaletteProviders to OHLC Series

The same technique can be applied to OHLC Bars. Just make sure when creating a class that inherits DefaultPaletteProvide that the overrideStrokeArgb function is defined. As usual, return a color in ARGB format to override this point stroke, else, return undefined for default stroke.